#pragma once
#include <algorithm>
#include <limits>

namespace icehpc
{
    namespace detail
    {
        double constexpr sqrt_newton_raphson(double x, double curr, double prev)
        {
            return curr == prev
                ? curr
                : sqrt_newton_raphson(x, 0.5 * (curr + x / curr), curr);
        }
    }

    namespace tools
    {
        template <class T>
        inline T abs(T x) { return x < 0 ? -x : x; }

        inline bool inrange(int x, int l, int r) { return l <= x && x <= r; }

        inline double constexpr sqrt(double x)
        {
            return 0 <= x && x < std::numeric_limits<double>::infinity()
                ? detail::sqrt_newton_raphson(x, x, 0)
                : std::numeric_limits<double>::quiet_NaN();
        }

        inline int start_id(int n, int size, int rank)
        {
            auto len = n / size + (n % size != 0);
            return rank * len;
        }

        inline int end_id(int n, int size, int rank)
        {
            auto id = start_id(n, size, rank + 1) - 1;
            return std::min(id, n - 1);
        }

        inline int count(int n, int size)
        {
            return n / size + (n % size != 0);
        }

        inline int identity(int n, int i, int size, int rank)
        {
            return start_id(n, size, rank) + i;
        }
    }
}

